/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package puzzleevolution;

/**
 *
 * @author vorispe1
 */
public class Chromosome {
    private int id;
    private float fitness;
    private PuzzlePiece[][] puzzlePossibleSolution;

    /**
     * Konstruktor prazdny
     */
    public Chromosome(){}


    /**
     * Konstruktor kopirujujici objekt Chromosome
     */
    public Chromosome(Chromosome original){
        this.id = original.id;
        PuzzlePiece[] originalDilky = new PuzzlePiece[Config.pocetDilkuX * Config.pocetDilkuY];
        PuzzlePiece[] copyDilky = new PuzzlePiece[Config.pocetDilkuX * Config.pocetDilkuY];

        //prevedeme puvodni matici reseni na pole
        originalDilky = original.toArray(original.getPuzzlePossibleSolution());

        //zkopirujeme pole takto, abychom nekopirovali pouze odkaz na puvodni
        System.arraycopy(originalDilky, 0, copyDilky, 0, Config.pocetDilkuX * Config.pocetDilkuY);

        //zkopirovane pole prevedeme zpet na matici a vlozime do jedince
        this.puzzlePossibleSolution = original.toMatrix(copyDilky);
        //navrhovane reseni zustava stejne, proto se ani jeho fitness nemeni
        this.fitness = original.fitness;
    }


    /**
     * Konstruktor z matice dilku
     */
    public Chromosome(PuzzlePiece[][] puzzlePossibleSolution){
        this.puzzlePossibleSolution = puzzlePossibleSolution;
    }
    

    /**
     * ruzne metody vypocitavani fitness jednoho jedince
     *
     * @return
     */
    public void calculateFitness (){
        PuzzlePiece[][] dilky = this.getPuzzlePossibleSolution();
        float fitnes = 0;
        float rozdily = 0;

if (Config.verzeFitness == 0){
        //soucet manhattanskych vzdalenosti dilku od svych spravnych mist - soucet rozdilu jejich hodnot souradnic x a y.
        fitnes = 0;

        for (int i=0; i < Config.pocetDilkuX ; i++){
            for (int j=0; j < Config.pocetDilkuY; j++){
                int x, y;

                //spravne souradnice dilku tohoto reseni
                x = ((dilky[i][j].getId()-1) / Config.pocetDilkuX);
                y = ((dilky[i][j].getId()-1) % Config.pocetDilkuY);

                fitnes += Math.abs(x - i) + Math.abs(y - j);
            }
        }
        this.setFitness(fitnes);
}
else if (Config.verzeFitness == 1){
        //soucet rozdilu RGB hodnot sousednich dilku.
        rozdily = 0;
        int penalizace = Config.delkaDilku; //penalizace na zvyrazneni rozdilu mezi rohy dilku

        for (int i=0; i < Config.pocetDilkuX ; i++){ //Config.pocetDilkuX
            for (int j=0; j < Config.pocetDilkuY; j++){ //Config.pocetDilkuY
                for (int k=0; k < (Config.delkaDilku); k++){
                    //rozdily mezi dilky v radcich
                    if(i < Config.pocetDilkuY - 1){
                        rozdily += Math.abs(dilky[i][j].getRight()[k].getRed()   - dilky[i+1][j].getLeft()[k].getRed());
                        rozdily += Math.abs(dilky[i][j].getRight()[k].getGreen() - dilky[i+1][j].getLeft()[k].getGreen());
                        rozdily += Math.abs(dilky[i][j].getRight()[k].getBlue()  - dilky[i+1][j].getLeft()[k].getBlue());
                    }
                    //rozdily mezi dilky ve sloupcich
                    if(j < Config.pocetDilkuX - 1){
                        rozdily += Math.abs(dilky[i][j].getDown()[k].getRed()   - dilky[i][j+1].getUp()[k].getRed());
                        rozdily += Math.abs(dilky[i][j].getDown()[k].getGreen() - dilky[i][j+1].getUp()[k].getGreen());
                        rozdily += Math.abs(dilky[i][j].getDown()[k].getBlue()  - dilky[i][j+1].getUp()[k].getBlue());
                    }
                }
                    //rozdily mezi rohem dilku s jeho diagonalnim sousedem dole vpravo
                    if((i < Config.pocetDilkuX - 1) & (j < Config.pocetDilkuY - 1)){
                        rozdily += Math.abs(dilky[i][j].getDown()[Config.delkaDilku-1].getRed()     - dilky[i+1][j+1].getUp()[0].getRed())   * penalizace;
                        rozdily += Math.abs(dilky[i][j].getDown()[Config.delkaDilku-1].getGreen()   - dilky[i+1][j+1].getUp()[0].getGreen()) * penalizace;
                        rozdily += Math.abs(dilky[i][j].getDown()[Config.delkaDilku-1].getBlue()    - dilky[i+1][j+1].getUp()[0].getBlue())  * penalizace;
                    }
                    //rozdily mezi rohem dilku s jeho diagonalnim sousedem nahore vpravo
                    if((i < Config.pocetDilkuX - 1) & (j < Config.pocetDilkuY - 1)){
                        rozdily += Math.abs(dilky[i][j+1].getUp()[Config.delkaDilku-1].getRed()     - dilky[i+1][j].getDown()[0].getRed())   * penalizace;
                        rozdily += Math.abs(dilky[i][j+1].getUp()[Config.delkaDilku-1].getGreen()   - dilky[i+1][j].getDown()[0].getGreen()) * penalizace;
                        rozdily += Math.abs(dilky[i][j+1].getUp()[Config.delkaDilku-1].getBlue()    - dilky[i+1][j].getDown()[0].getBlue())  * penalizace;
                    }
            }
        }
        this.setFitness(rozdily);
}
else if (Config.verzeFitness == 2){
        //soucet rozdilu HSB hodnot sousednich dilku.
        rozdily = 0;
        int penalizace = Config.delkaDilku; //penalizace na zvyrazneni rozdilu mezi rohy dilku

        for (int i=0; i < Config.pocetDilkuX ; i++){
            for (int j=0; j < Config.pocetDilkuY; j++){
                for (int k=0; k < Config.delkaDilku; k++){
                    //rozdily mezi dilky v radcich
                    if(i < Config.pocetDilkuY - 1){
                        rozdily += Math.abs(dilky[i][j].getRight()[k].getHue()   - dilky[i+1][j].getLeft()[k].getHue());
                        rozdily += Math.abs(dilky[i][j].getRight()[k].getSaturation() - dilky[i+1][j].getLeft()[k].getSaturation());
                        rozdily += Math.abs(dilky[i][j].getRight()[k].getBrightness()  - dilky[i+1][j].getLeft()[k].getBrightness());
                    }
                    //rozdily mezi dilky ve sloupcich
                    if(j < Config.pocetDilkuX - 1){
                        rozdily += Math.abs(dilky[i][j].getDown()[k].getHue()   - dilky[i][j+1].getUp()[k].getHue());
                        rozdily += Math.abs(dilky[i][j].getDown()[k].getSaturation() - dilky[i][j+1].getUp()[k].getSaturation());
                        rozdily += Math.abs(dilky[i][j].getDown()[k].getBrightness()  - dilky[i][j+1].getUp()[k].getBrightness());
                    }
                }
                    //rozdily mezi rohem dilku s jeho diagonalnim sousedem dole vpravo
                    if((i < Config.pocetDilkuX - 1) & (j < Config.pocetDilkuY - 1)){
                        rozdily += Math.abs(dilky[i][j].getDown()[Config.delkaDilku-1].getHue()     - dilky[i+1][j+1].getUp()[0].getHue())   * penalizace;
                        rozdily += Math.abs(dilky[i][j].getDown()[Config.delkaDilku-1].getSaturation()   - dilky[i+1][j+1].getUp()[0].getSaturation()) * penalizace;
                        rozdily += Math.abs(dilky[i][j].getDown()[Config.delkaDilku-1].getBrightness()    - dilky[i+1][j+1].getUp()[0].getBrightness())  * penalizace;
                    }
                    //rozdily mezi rohem dilku s jeho diagonalnim sousedem nahore vpravo
                    if((i < Config.pocetDilkuX - 1) & (j < Config.pocetDilkuY - 1)){
                        rozdily += Math.abs(dilky[i][j+1].getUp()[Config.delkaDilku-1].getHue()     - dilky[i+1][j].getDown()[0].getHue())   * penalizace;
                        rozdily += Math.abs(dilky[i][j+1].getUp()[Config.delkaDilku-1].getSaturation()   - dilky[i+1][j].getDown()[0].getSaturation()) * penalizace;
                        rozdily += Math.abs(dilky[i][j+1].getUp()[Config.delkaDilku-1].getBrightness()    - dilky[i+1][j].getDown()[0].getBrightness())  * penalizace;
                    }
            }
        }
        this.setFitness(rozdily);
}
else if (Config.verzeFitness == 3){
        //soucet alfa hodnot na vnejsim okraji puzzle.
        float alfa = 0;
        fitnes = 0;

        //scitani pruhlednosti na vnejsim okraji pro nxn velke puzzle (ne mxn)
        for (int i=0; i < Config.pocetDilkuX; i++){
                for (int k=0; k < Config.delkaDilku; k++){
                    alfa += Math.abs(dilky[i][0].getUp()[k].getAlfa());

                    alfa += Math.abs(dilky[i][Config.pocetDilkuX - 1].getDown()[k].getAlfa());

                    alfa += Math.abs(dilky[0][i].getLeft()[k].getAlfa());

                    alfa += Math.abs(dilky[Config.pocetDilkuX - 1][i].getRight()[k].getAlfa());
            }
        }
        this.setFitness(alfa);
}
else if (Config.verzeFitness == 4){
        //soucet rozdilu RGB hodnot sousednich dilku + soucet alfa hodnot na vnejsim okraji puzzle.
        float alfa = 0;
        int penalizace = Config.delkaDilku;   //penalizace na zvyrazneni rozdilu mezi rohy dilku
        fitnes = 0;

        //scitani pruhlednosti na vnejsim okraji pro nxn velke puzzle (ne mxn)
        for (int i=0; i < Config.pocetDilkuX; i++){//Config.pocetDilkuX
                for (int k=0; k < Config.delkaDilku; k++){
                    alfa += Math.abs(dilky[i][0].getUp()[k].getAlfa());

                    alfa += Math.abs(dilky[i][Config.pocetDilkuX - 1].getDown()[k].getAlfa());

                    alfa += Math.abs(dilky[0][i].getLeft()[k].getAlfa());

                    alfa += Math.abs(dilky[Config.pocetDilkuX - 1][i].getRight()[k].getAlfa());
            }
        }

        fitnes += alfa * penalizace;

        //scitani rozdilu hran sousedicich dilku
        for (int i=0; i < Config.pocetDilkuX ; i++){
            for (int j=0; j < Config.pocetDilkuY; j++){
                for (int k=0; k < Config.delkaDilku; k++){
                    //rozdily mezi dilky v radcich
                    if(j < Config.pocetDilkuY - 1){
                        rozdily += Math.abs(dilky[i][j].getRight()[k].getRed()   - dilky[i][j+1].getLeft()[k].getRed());
                        rozdily += Math.abs(dilky[i][j].getRight()[k].getGreen() - dilky[i][j+1].getLeft()[k].getGreen());
                        rozdily += Math.abs(dilky[i][j].getRight()[k].getBlue()  - dilky[i][j+1].getLeft()[k].getBlue());
                    }
                    //rozdily mezi dilky ve sloupcich
                    if(i < Config.pocetDilkuX - 1){
                        rozdily += Math.abs(dilky[i][j].getRight()[k].getRed()   - dilky[i+1][j].getLeft()[k].getRed());
                        rozdily += Math.abs(dilky[i][j].getRight()[k].getGreen() - dilky[i+1][j].getLeft()[k].getGreen());
                        rozdily += Math.abs(dilky[i][j].getRight()[k].getBlue()  - dilky[i+1][j].getLeft()[k].getBlue());
                    }
                }
                    //rozdily mezi rohem dilku s jeho diagonalnim sousedem dole vpravo
                    if((i < Config.pocetDilkuX - 1) & (j < Config.pocetDilkuY - 1)){
                        rozdily += Math.abs(dilky[i][j].getDown()[Config.delkaDilku-1].getRed()     - dilky[i+1][j+1].getUp()[0].getRed())   * penalizace;
                        rozdily += Math.abs(dilky[i][j].getDown()[Config.delkaDilku-1].getGreen()   - dilky[i+1][j+1].getUp()[0].getGreen()) * penalizace;
                        rozdily += Math.abs(dilky[i][j].getDown()[Config.delkaDilku-1].getBlue()    - dilky[i+1][j+1].getUp()[0].getBlue())  * penalizace;
                    }
                    //rozdily mezi rohem dilku s jeho diagonalnim sousedem nahore vpravo
                    if((i < Config.pocetDilkuX - 1) & (j < Config.pocetDilkuY - 1)){
                        rozdily += Math.abs(dilky[i][j+1].getUp()[Config.delkaDilku-1].getRed()     - dilky[i+1][j].getDown()[0].getRed())   * penalizace;
                        rozdily += Math.abs(dilky[i][j+1].getUp()[Config.delkaDilku-1].getGreen()   - dilky[i+1][j].getDown()[0].getGreen()) * penalizace;
                        rozdily += Math.abs(dilky[i][j+1].getUp()[Config.delkaDilku-1].getBlue()    - dilky[i+1][j].getDown()[0].getBlue())  * penalizace;
                    }
            }
        }
        fitnes += rozdily;
        this.setFitness(fitnes);
}
else if (Config.verzeFitness == 5){
        //soucet rozdilu HSB hodnot sousednich dilku + soucet alfa hodnot na vnejsim okraji puzzle.
        float alfa = 0;
        rozdily = 0;
        int penalizace = Config.delkaDilku; //penalizace na zvyrazneni rozdilu mezi rohy dilku
        fitnes = 0;

        //scitani pruhlednosti na vnejsim okraji pro nxn velke puzzle (ne mxn)
        for (int i=0; i < Config.pocetDilkuX; i++){//Config.pocetDilkuX
                for (int k=0; k < Config.delkaDilku; k++){
                    alfa += Math.abs(dilky[i][0].getUp()[k].getAlfa());

                    alfa += Math.abs(dilky[i][Config.pocetDilkuX - 1].getDown()[k].getAlfa());

                    alfa += Math.abs(dilky[0][i].getLeft()[k].getAlfa());

                    alfa += Math.abs(dilky[Config.pocetDilkuX - 1][i].getRight()[k].getAlfa());
            }
        }

        fitnes += alfa * penalizace;

        for (int i=0; i < Config.pocetDilkuX ; i++){
            for (int j=0; j < Config.pocetDilkuY; j++){
                for (int k=0; k < Config.delkaDilku; k++){
                    //rozdily mezi dilky v radcich
                    if(i < Config.pocetDilkuY - 1){
                        rozdily += Math.abs(dilky[i][j].getRight()[k].getHue()   - dilky[i+1][j].getLeft()[k].getHue());
                        rozdily += Math.abs(dilky[i][j].getRight()[k].getSaturation() - dilky[i+1][j].getLeft()[k].getSaturation());
                        rozdily += Math.abs(dilky[i][j].getRight()[k].getBrightness()  - dilky[i+1][j].getLeft()[k].getBrightness());
                    }
                    //rozdily mezi dilky ve sloupcich
                    if(j < Config.pocetDilkuX - 1){
                        rozdily += Math.abs(dilky[i][j].getDown()[k].getHue()   - dilky[i][j+1].getUp()[k].getHue());
                        rozdily += Math.abs(dilky[i][j].getDown()[k].getSaturation() - dilky[i][j+1].getUp()[k].getSaturation());
                        rozdily += Math.abs(dilky[i][j].getDown()[k].getBrightness()  - dilky[i][j+1].getUp()[k].getBrightness());
                    }
                }
                    //rozdily mezi rohem dilku s jeho diagonalnim sousedem dole vpravo
                    if((i < Config.pocetDilkuX - 1) & (j < Config.pocetDilkuY - 1)){
                        rozdily += Math.abs(dilky[i][j].getDown()[Config.delkaDilku-1].getHue()     - dilky[i+1][j+1].getUp()[0].getHue())   * penalizace;
                        rozdily += Math.abs(dilky[i][j].getDown()[Config.delkaDilku-1].getSaturation()   - dilky[i+1][j+1].getUp()[0].getSaturation()) * penalizace;
                        rozdily += Math.abs(dilky[i][j].getDown()[Config.delkaDilku-1].getBrightness()    - dilky[i+1][j+1].getUp()[0].getBrightness())  * penalizace;
                    }
                    //rozdily mezi rohem dilku s jeho diagonalnim sousedem nahore vpravo
                    if((i < Config.pocetDilkuX - 1) & (j < Config.pocetDilkuY - 1)){
                        rozdily += Math.abs(dilky[i][j+1].getUp()[Config.delkaDilku-1].getHue()     - dilky[i+1][j].getDown()[0].getHue())   * penalizace;
                        rozdily += Math.abs(dilky[i][j+1].getUp()[Config.delkaDilku-1].getSaturation()   - dilky[i+1][j].getDown()[0].getSaturation()) * penalizace;
                        rozdily += Math.abs(dilky[i][j+1].getUp()[Config.delkaDilku-1].getBrightness()    - dilky[i+1][j].getDown()[0].getBrightness())  * penalizace;
                    }
            }
        }

        fitnes += rozdily;

        this.setFitness(fitnes);
}
else if (Config.verzeFitness == 6){
        //RGB jako souradnice bodu v prostoru. fitnes je soucet vzdalenosti bodu v prostoru. Bod 1 je hodnota hrany jednoho dilku a Bod dva je z druheho dilku
        fitnes = 0;
        rozdily = 0;
        double r = 0, g = 0, b = 0;
        float alfa = 0;
        int penalizace = Config.delkaDilku;   //penalizace na zvyrazneni rozdilu mezi rohy dilku

        //scitani pruhlednosti na vnejsim okraji pro nxn velke puzzle (ne mxn)
        for (int i=0; i < Config.pocetDilkuX; i++){//Config.pocetDilkuX
                for (int k=0; k < Config.delkaDilku; k++){
                    alfa += Math.abs(dilky[i][0].getUp()[k].getAlfa());

                    alfa += Math.abs(dilky[i][Config.pocetDilkuX - 1].getDown()[k].getAlfa());

                    alfa += Math.abs(dilky[0][i].getLeft()[k].getAlfa());

                    alfa += Math.abs(dilky[Config.pocetDilkuX - 1][i].getRight()[k].getAlfa());
            }
        }

        fitnes += alfa * penalizace;

        //scitani rozdilu hran sousedicich dilku - RGB model
        for (int i=0; i < Config.pocetDilkuX ; i++){
            for (int j=0; j < Config.pocetDilkuY - 1; j++){
                for (int k=0; k < Config.delkaDilku; k++){
                    //rozdily mezi dilky v radcich
                    if(j < Config.pocetDilkuY - 1){
                        r = Math.abs(dilky[i][j].getRight()[k].getRed()   - dilky[i][j+1].getLeft()[k].getRed());
                        g = Math.abs(dilky[i][j].getRight()[k].getGreen() - dilky[i][j+1].getLeft()[k].getGreen());
                        b = Math.abs(dilky[i][j].getRight()[k].getBlue()  - dilky[i][j+1].getLeft()[k].getBlue());
                    }
                    fitnes += Math.sqrt( r*r + g*g + b*b );

                    //rozdily mezi dilky ve sloupcich
                    if(i < Config.pocetDilkuX - 1){
                        r = Math.abs(dilky[i][j].getRight()[k].getRed()   - dilky[i+1][j].getLeft()[k].getRed());
                        g = Math.abs(dilky[i][j].getRight()[k].getGreen() - dilky[i+1][j].getLeft()[k].getGreen());
                        b = Math.abs(dilky[i][j].getRight()[k].getBlue()  - dilky[i+1][j].getLeft()[k].getBlue());
                    }
                    fitnes += Math.sqrt( r*r + g*g + b*b );
                }
                    //rozdily mezi rohem dilku s jeho diagonalnim sousedem dole vpravo
                    if((i < Config.pocetDilkuX - 1) & (j < Config.pocetDilkuY - 1)){
                        r = Math.abs(dilky[i][j].getDown()[Config.delkaDilku-1].getRed()     - dilky[i+1][j+1].getUp()[0].getRed())   * penalizace;
                        g = Math.abs(dilky[i][j].getDown()[Config.delkaDilku-1].getGreen()   - dilky[i+1][j+1].getUp()[0].getGreen()) * penalizace;
                        b = Math.abs(dilky[i][j].getDown()[Config.delkaDilku-1].getBlue()    - dilky[i+1][j+1].getUp()[0].getBlue())  * penalizace;
                    }
                    fitnes += Math.sqrt( r*r + g*g + b*b );

                    //rozdily mezi rohem dilku s jeho diagonalnim sousedem nahore vpravo
                    if((i < Config.pocetDilkuX - 1) & (j < Config.pocetDilkuY - 1)){
                        r = Math.abs(dilky[i][j+1].getUp()[Config.delkaDilku-1].getRed()     - dilky[i+1][j].getDown()[0].getRed())   * penalizace;
                        g = Math.abs(dilky[i][j+1].getUp()[Config.delkaDilku-1].getGreen()   - dilky[i+1][j].getDown()[0].getGreen()) * penalizace;
                        b = Math.abs(dilky[i][j+1].getUp()[Config.delkaDilku-1].getBlue()    - dilky[i+1][j].getDown()[0].getBlue())  * penalizace;
                    }
                    fitnes += Math.sqrt( r*r + g*g + b*b );
                }
        }
        this.setFitness(fitnes);
    }
}

    /**
     *
     *
     * @param vstupniHrana
     * @return
     */
    public int[] puzzlePieceEdgeToRGBarray (Pixel[] vstupniHrana){
        int[] rgbArray = new int[Config.delkaDilku * 3];

        for(int i = 0; i < Config.delkaDilku; i++){
            rgbArray[3*i] = vstupniHrana[i].getRed();
            rgbArray[3*i+1] = vstupniHrana[i].getBlue();
            rgbArray[3*i+2] = vstupniHrana[i].getGreen();
        }

        return rgbArray;
    }

    /**
     *
     *
     * @param vstupniHrana
     * @return
     */
    public double[] puzzlePieceEdgeToBrightnessArray (Pixel[] vstupniHrana){
        double[] rArray = new double[Config.delkaDilku];

        for(int i = 0; i < Config.delkaDilku; i++){
            rArray[i] = vstupniHrana[i].getBrightness();
        }

        return rArray;
    }

    /**
     * Metoda seradi dilky jedince z pole do matice
     *
     * @param pole
     * @return
     */
    public PuzzlePiece[][] toMatrix (PuzzlePiece[] pole){
        PuzzlePiece[][] matice = new PuzzlePiece[Config.pocetDilkuX][Config.pocetDilkuY];
        int o = 0;

        for (int i=0; i < Config.pocetDilkuX; i++){
            for (int j=0; j < Config.pocetDilkuY; j++){
                matice[i][j] = pole[o++];
            }
        }
        return matice;
    }

    /**
     * Metoda seradi dilky jedince z matice do pole
     *
     * @param pole
     * @return
     */
    public PuzzlePiece[] toArray (PuzzlePiece[][] matice){
        PuzzlePiece[] pole = new PuzzlePiece[Config.pocetDilkuX * Config.pocetDilkuY];
        int o = 0;

        for (int i=0; i < Config.pocetDilkuX; i++){
            for (int j=0; j < Config.pocetDilkuY; j++){
                pole[o++] = matice[i][j];
            }
        }

        return pole;
    }


    
    /**
     * @return the id
     */
    public int getId() {
        return id;
    }

    /**
     * @param id the id to set
     */
    public void setId(int id) {
        this.id = id;
    }

    /**
     * @return the fitness
     */
    public float getFitness() {
        return fitness;
    }

    /**
     * @param fitness the fitness to set
     */
    public void setFitness(float fitness) {
        this.fitness = fitness;
    }

    /**
     * @return the puzzlePossibleSolution
     */
    public PuzzlePiece[][] getPuzzlePossibleSolution() {
        return puzzlePossibleSolution;
    }

    /**
     * @param puzzlePossibleSolution the puzzlePossibleSolution to set
     */
    public void setPuzzlePossibleSolution(PuzzlePiece[][] puzzlePossibleSolution) {
        this.puzzlePossibleSolution = puzzlePossibleSolution;
    }

}
